/*
Copyright 2019 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package sqlparser

import (
	"fmt"
	"strings"

	"vitess.io/vitess/go/sqltypes"
)

/*
This is the Vitess AST. This file should only contain pure struct declarations,
or methods used to mark a struct as implementing an interface. All other methods
related to these structs live in ast_funcs.go
*/

// SQLNode defines the interface for all nodes
// generated by the parser.
type SQLNode interface {
	Format(buf *TrackedBuffer)
}

// Statements
type (
	// Statement represents a statement.
	Statement interface {
		iStatement()
		SQLNode
	}

	// SelectStatement any SELECT statement.
	SelectStatement interface {
		iSelectStatement()
		iStatement()
		iInsertRows()
		AddOrder(*Order)
		SetLimit(*Limit)
		SetLock(lock Lock)
		SQLNode
	}

	// Select represents a SELECT statement.
	Select struct {
		Cache            *bool // a reference here so it can be nil
		Distinct         bool
		StraightJoinHint bool
		SQLCalcFoundRows bool
		Comments         Comments
		SelectExprs      SelectExprs
		From             TableExprs
		Where            *Where
		GroupBy          GroupBy
		Having           *Where
		OrderBy          OrderBy
		Limit            *Limit
		Lock             Lock
		IntoOutfileS3    string
	}

	// Lock is an enum for the type of lock in the statement
	Lock int8

	// UnionSelect represents union type and select statement after first select statement.
	UnionSelect struct {
		Type      UnionType
		Statement SelectStatement
	}

	// UnionType is the type of union
	UnionType int8

	// Union represents a UNION statement.
	Union struct {
		FirstStatement SelectStatement
		UnionSelects   []*UnionSelect
		OrderBy        OrderBy
		Limit          *Limit
		Lock           Lock
	}

	// VStream represents a VSTREAM statement.
	VStream struct {
		Comments   Comments
		SelectExpr SelectExpr
		Table      TableName
		Where      *Where
		Limit      *Limit
	}

	// Stream represents a SELECT statement.
	Stream struct {
		Comments   Comments
		SelectExpr SelectExpr
		Table      TableName
	}

	// Insert represents an INSERT or REPLACE statement.
	// Per the MySQL docs, http://dev.mysql.com/doc/refman/5.7/en/replace.html
	// Replace is the counterpart to `INSERT IGNORE`, and works exactly like a
	// normal INSERT except if the row exists. In that case it first deletes
	// the row and re-inserts with new values. For that reason we keep it as an Insert struct.
	// Replaces are currently disallowed in sharded schemas because
	// of the implications the deletion part may have on vindexes.
	// If you add fields here, consider adding them to calls to validateUnshardedRoute.
	Insert struct {
		Action     InsertAction
		Comments   Comments
		Ignore     Ignore
		Table      TableName
		Partitions Partitions
		Columns    Columns
		Rows       InsertRows
		OnDup      OnDup
	}

	// Ignore represents whether ignore was specified or not
	Ignore bool

	// InsertAction is the action for insert.
	InsertAction int8

	// Update represents an UPDATE statement.
	// If you add fields here, consider adding them to calls to validateUnshardedRoute.
	Update struct {
		Comments   Comments
		Ignore     Ignore
		TableExprs TableExprs
		Exprs      UpdateExprs
		Where      *Where
		OrderBy    OrderBy
		Limit      *Limit
	}

	// Delete represents a DELETE statement.
	// If you add fields here, consider adding them to calls to validateUnshardedRoute.
	Delete struct {
		Ignore     Ignore
		Comments   Comments
		Targets    TableNames
		TableExprs TableExprs
		Partitions Partitions
		Where      *Where
		OrderBy    OrderBy
		Limit      *Limit
	}

	// Set represents a SET statement.
	Set struct {
		Comments Comments
		Exprs    SetExprs
	}

	// SetTransaction represents a SET TRANSACTION statement.
	SetTransaction struct {
		SQLNode
		Comments        Comments
		Scope           Scope
		Characteristics []Characteristic
	}

	// Scope is an enum for scope of query
	Scope int8

	// Characteristic is a transaction related change
	Characteristic interface {
		SQLNode
		iChar()
	}

	// IsolationLevel is an enum for isolation levels
	IsolationLevel int8

	// AccessMode is enum for the mode - ReadOnly or ReadWrite
	AccessMode int8

	// DBDDL represents a CREATE, DROP, or ALTER database statement.
	DBDDL struct {
		Action      DBDDLAction
		DBName      string
		IfExists    bool
		IfNotExists bool
		Collate     string
		Charset     string
	}

	// DDLStrategy suggests how an ALTER TABLE should run (e.g. "" for normal, "gh-ost" or "pt-osc")
	DDLStrategy string

	// OnlineDDLHint indicates strategy and options for running an online DDL
	OnlineDDLHint struct {
		Strategy DDLStrategy
		Options  string
	}
	// DBDDLAction is an enum for DBDDL Actions
	DBDDLAction int8

	// DDL represents a CREATE, ALTER, DROP, RENAME, TRUNCATE or ANALYZE statement.
	DDL struct {
		Action DDLAction

		// FromTables is set if Action is RenameDDLAction or DropDDLAction.
		FromTables TableNames

		// ToTables is set if Action is RenameDDLAction.
		ToTables TableNames

		// Table is set if Action is other than RenameDDLAction or DropDDLAction.
		Table TableName

		// The following fields are set if a DDL was fully analyzed.
		IfExists      bool
		TableSpec     *TableSpec
		OptLike       *OptLike
		PartitionSpec *PartitionSpec
		OnlineHint    *OnlineDDLHint

		// VindexSpec is set for CreateVindexDDLAction, DropVindexDDLAction, AddColVindexDDLAction, DropColVindexDDLAction.
		VindexSpec *VindexSpec

		// VindexCols is set for AddColVindexDDLAction.
		VindexCols []ColIdent

		// AutoIncSpec is set for AddAutoIncDDLAction.
		AutoIncSpec *AutoIncSpec
	}

	// DDLAction is an enum for DDL.Action
	DDLAction int8

	// ParenSelect is a parenthesized SELECT statement.
	ParenSelect struct {
		Select SelectStatement
	}

	// Show represents a show statement.
	Show struct {
		Extended               string
		Type                   string
		OnTable                TableName
		Table                  TableName
		ShowTablesOpt          *ShowTablesOpt
		Scope                  Scope
		ShowCollationFilterOpt Expr
	}

	// ShowTableStatus is a struct for SHOW TABLE STATUS queries.
	ShowTableStatus struct {
		DatabaseName string
		Filter       *ShowFilter
	}

	// Use represents a use statement.
	Use struct {
		DBName TableIdent
	}

	// Begin represents a Begin statement.
	Begin struct{}

	// Commit represents a Commit statement.
	Commit struct{}

	// Rollback represents a Rollback statement.
	Rollback struct{}

	// SRollback represents a rollback to savepoint statement.
	SRollback struct {
		Name ColIdent
	}

	// Savepoint represents a savepoint statement.
	Savepoint struct {
		Name ColIdent
	}

	// Release represents a release savepoint statement.
	Release struct {
		Name ColIdent
	}

	// Explain represents an EXPLAIN statement
	Explain struct {
		Type      ExplainType
		Statement Statement
	}

	// ExplainType is an enum for Explain.Type
	ExplainType int8

	// OtherRead represents a DESCRIBE, or EXPLAIN statement.
	// It should be used only as an indicator. It does not contain
	// the full AST for the statement.
	OtherRead struct{}

	// OtherAdmin represents a misc statement that relies on ADMIN privileges,
	// such as REPAIR, OPTIMIZE, or TRUNCATE statement.
	// It should be used only as an indicator. It does not contain
	// the full AST for the statement.
	OtherAdmin struct{}
)

func (*Union) iStatement()             {}
func (*Select) iStatement()            {}
func (*Stream) iStatement()            {}
func (*VStream) iStatement()           {}
func (*Insert) iStatement()            {}
func (*Update) iStatement()            {}
func (*Delete) iStatement()            {}
func (*Set) iStatement()               {}
func (*SetTransaction) iStatement()    {}
func (*DBDDL) iStatement()             {}
func (*DDL) iStatement()               {}
func (*Show) iStatement()              {}
func (*Use) iStatement()               {}
func (*Begin) iStatement()             {}
func (*Commit) iStatement()            {}
func (*Rollback) iStatement()          {}
func (*SRollback) iStatement()         {}
func (*Savepoint) iStatement()         {}
func (*Release) iStatement()           {}
func (*Explain) iStatement()           {}
func (*OtherRead) iStatement()         {}
func (*OtherAdmin) iStatement()        {}
func (*Select) iSelectStatement()      {}
func (*Union) iSelectStatement()       {}
func (*ParenSelect) iSelectStatement() {}
func (*ShowTableStatus) iStatement()   {}

// ParenSelect can actually not be a top level statement,
// but we have to allow it because it's a requirement
// of SelectStatement.
func (*ParenSelect) iStatement() {}

// InsertRows represents the rows for an INSERT statement.
type InsertRows interface {
	iInsertRows()
	SQLNode
}

func (*Select) iInsertRows()      {}
func (*Union) iInsertRows()       {}
func (Values) iInsertRows()       {}
func (*ParenSelect) iInsertRows() {}

// OptLike works for create table xxx like xxx
type OptLike struct {
	LikeTable TableName
}

// PartitionSpec describe partition actions (for alter and create)
type PartitionSpec struct {
	Action      PartitionSpecAction
	Name        ColIdent
	Definitions []*PartitionDefinition
}

// PartitionSpecAction is an enum for PartitionSpec.Action
type PartitionSpecAction int8

// PartitionDefinition describes a very minimal partition definition
type PartitionDefinition struct {
	Name     ColIdent
	Limit    Expr
	Maxvalue bool
}

// TableSpec describes the structure of a table from a CREATE TABLE statement
type TableSpec struct {
	Columns     []*ColumnDefinition
	Indexes     []*IndexDefinition
	Constraints []*ConstraintDefinition
	Options     string
}

// ColumnDefinition describes a column in a CREATE TABLE statement
type ColumnDefinition struct {
	Name ColIdent
	// TODO: Should this not be a reference?
	Type ColumnType
}

// ColumnType represents a sql type in a CREATE TABLE statement
// All optional fields are nil if not specified
type ColumnType struct {
	// The base type string
	Type string

	// Generic field options.
	NotNull       bool
	Autoincrement bool
	Default       Expr
	OnUpdate      Expr
	Comment       *Literal

	// Numeric field options
	Length   *Literal
	Unsigned bool
	Zerofill bool
	Scale    *Literal

	// Text field options
	Charset string
	Collate string

	// Enum values
	EnumValues []string

	// Key specification
	KeyOpt ColumnKeyOption
}

// IndexDefinition describes an index in a CREATE TABLE statement
type IndexDefinition struct {
	Info    *IndexInfo
	Columns []*IndexColumn
	Options []*IndexOption
}

// IndexInfo describes the name and type of an index in a CREATE TABLE statement
type IndexInfo struct {
	Type    string
	Name    ColIdent
	Primary bool
	Spatial bool
	Unique  bool
}

// VindexSpec defines a vindex for a CREATE VINDEX or DROP VINDEX statement
type VindexSpec struct {
	Name   ColIdent
	Type   ColIdent
	Params []VindexParam
}

// AutoIncSpec defines and autoincrement value for a ADD AUTO_INCREMENT statement
type AutoIncSpec struct {
	Column   ColIdent
	Sequence TableName
}

// VindexParam defines a key/value parameter for a CREATE VINDEX statement
type VindexParam struct {
	Key ColIdent
	Val string
}

// ConstraintDefinition describes a constraint in a CREATE TABLE statement
type ConstraintDefinition struct {
	Name    string
	Details ConstraintInfo
}

type (
	// ConstraintInfo details a constraint in a CREATE TABLE statement
	ConstraintInfo interface {
		SQLNode
		iConstraintInfo()
	}

	// ForeignKeyDefinition describes a foreign key in a CREATE TABLE statement
	ForeignKeyDefinition struct {
		Source            Columns
		ReferencedTable   TableName
		ReferencedColumns Columns
		OnDelete          ReferenceAction
		OnUpdate          ReferenceAction
	}
)

// ShowFilter is show tables filter
type ShowFilter struct {
	Like   string
	Filter Expr
}

// Comments represents a list of comments.
type Comments [][]byte

// SelectExprs represents SELECT expressions.
type SelectExprs []SelectExpr

type (
	// SelectExpr represents a SELECT expression.
	SelectExpr interface {
		iSelectExpr()
		SQLNode
	}

	// StarExpr defines a '*' or 'table.*' expression.
	StarExpr struct {
		TableName TableName
	}

	// AliasedExpr defines an aliased SELECT expression.
	AliasedExpr struct {
		Expr Expr
		As   ColIdent
	}

	// Nextval defines the NEXT VALUE expression.
	Nextval struct {
		Expr Expr
	}
)

func (*StarExpr) iSelectExpr()    {}
func (*AliasedExpr) iSelectExpr() {}
func (Nextval) iSelectExpr()      {}

// Columns represents an insert column list.
type Columns []ColIdent

// Partitions is a type alias for Columns so we can handle printing efficiently
type Partitions Columns

// TableExprs represents a list of table expressions.
type TableExprs []TableExpr

type (
	// TableExpr represents a table expression.
	TableExpr interface {
		iTableExpr()
		SQLNode
	}

	// AliasedTableExpr represents a table expression
	// coupled with an optional alias or index hint.
	// If As is empty, no alias was used.
	AliasedTableExpr struct {
		Expr       SimpleTableExpr
		Partitions Partitions
		As         TableIdent
		Hints      *IndexHints
	}

	// JoinTableExpr represents a TableExpr that's a JOIN operation.
	JoinTableExpr struct {
		LeftExpr  TableExpr
		Join      JoinType
		RightExpr TableExpr
		Condition JoinCondition
	}

	// JoinType represents the type of Join for JoinTableExpr
	JoinType int8

	// ParenTableExpr represents a parenthesized list of TableExpr.
	ParenTableExpr struct {
		Exprs TableExprs
	}
)

func (*AliasedTableExpr) iTableExpr() {}
func (*ParenTableExpr) iTableExpr()   {}
func (*JoinTableExpr) iTableExpr()    {}

type (
	// SimpleTableExpr represents a simple table expression.
	SimpleTableExpr interface {
		iSimpleTableExpr()
		SQLNode
	}

	// TableName represents a table  name.
	// Qualifier, if specified, represents a database or keyspace.
	// TableName is a value struct whose fields are case sensitive.
	// This means two TableName vars can be compared for equality
	// and a TableName can also be used as key in a map.
	TableName struct {
		Name, Qualifier TableIdent
	}

	// Subquery represents a subquery.
	Subquery struct {
		Select SelectStatement
	}
)

func (TableName) iSimpleTableExpr() {}
func (*Subquery) iSimpleTableExpr() {}

// TableNames is a list of TableName.
type TableNames []TableName

// JoinCondition represents the join conditions (either a ON or USING clause)
// of a JoinTableExpr.
type JoinCondition struct {
	On    Expr
	Using Columns
}

// IndexHints represents a list of index hints.
type IndexHints struct {
	Type    IndexHintsType
	Indexes []ColIdent
}

// IndexHintsType is an enum for IndexHints.Type
type IndexHintsType int8

// Where represents a WHERE or HAVING clause.
type Where struct {
	Type WhereType
	Expr Expr
}

// WhereType is an enum for Where.Type
type WhereType int8

// *********** Expressions
type (
	// Expr represents an expression.
	Expr interface {
		iExpr()
		SQLNode
	}

	// AndExpr represents an AND expression.
	AndExpr struct {
		Left, Right Expr
	}

	// OrExpr represents an OR expression.
	OrExpr struct {
		Left, Right Expr
	}

	// XorExpr represents an XOR expression.
	XorExpr struct {
		Left, Right Expr
	}

	// NotExpr represents a NOT expression.
	NotExpr struct {
		Expr Expr
	}

	// ComparisonExpr represents a two-value comparison expression.
	ComparisonExpr struct {
		Operator    ComparisonExprOperator
		Left, Right Expr
		Escape      Expr
	}

	// ComparisonExprOperator is an enum for ComparisonExpr.Operator
	ComparisonExprOperator int8

	// RangeCond represents a BETWEEN or a NOT BETWEEN expression.
	RangeCond struct {
		Operator RangeCondOperator
		Left     Expr
		From, To Expr
	}

	// RangeCondOperator is an enum for RangeCond.Operator
	RangeCondOperator int8

	// IsExpr represents an IS ... or an IS NOT ... expression.
	IsExpr struct {
		Operator IsExprOperator
		Expr     Expr
	}

	// IsExprOperator is an enum for IsExpr.Operator
	IsExprOperator int8

	// ExistsExpr represents an EXISTS expression.
	ExistsExpr struct {
		Subquery *Subquery
	}

	// Literal represents a fixed value.
	Literal struct {
		Type ValType
		Val  []byte
	}

	// Argument represents bindvariable expression
	Argument []byte

	// NullVal represents a NULL value.
	NullVal struct{}

	// BoolVal is true or false.
	BoolVal bool

	// ColName represents a column name.
	ColName struct {
		// Metadata is not populated by the parser.
		// It's a placeholder for analyzers to store
		// additional data, typically info about which
		// table or column this node references.
		Metadata  interface{}
		Name      ColIdent
		Qualifier TableName
	}

	// ColTuple represents a list of column values.
	// It can be ValTuple, Subquery, ListArg.
	ColTuple interface {
		iColTuple()
		Expr
	}

	// ListArg represents a named list argument.
	ListArg []byte

	// ValTuple represents a tuple of actual values.
	ValTuple Exprs

	// BinaryExpr represents a binary value expression.
	BinaryExpr struct {
		Operator    BinaryExprOperator
		Left, Right Expr
	}

	// BinaryExprOperator is an enum for BinaryExpr.Operator
	BinaryExprOperator int8

	// UnaryExpr represents a unary value expression.
	UnaryExpr struct {
		Operator UnaryExprOperator
		Expr     Expr
	}

	// UnaryExprOperator is an enum for UnaryExpr.Operator
	UnaryExprOperator int8

	// IntervalExpr represents a date-time INTERVAL expression.
	IntervalExpr struct {
		Expr Expr
		Unit string
	}

	// TimestampFuncExpr represents the function and arguments for TIMESTAMP{ADD,DIFF} functions.
	TimestampFuncExpr struct {
		Name  string
		Expr1 Expr
		Expr2 Expr
		Unit  string
	}

	// CollateExpr represents dynamic collate operator.
	CollateExpr struct {
		Expr    Expr
		Charset string
	}

	// FuncExpr represents a function call.
	FuncExpr struct {
		Qualifier TableIdent
		Name      ColIdent
		Distinct  bool
		Exprs     SelectExprs
	}

	// GroupConcatExpr represents a call to GROUP_CONCAT
	GroupConcatExpr struct {
		Distinct  bool
		Exprs     SelectExprs
		OrderBy   OrderBy
		Separator string
		Limit     *Limit
	}

	// ValuesFuncExpr represents a function call.
	ValuesFuncExpr struct {
		Name *ColName
	}

	// SubstrExpr represents a call to SubstrExpr(column, value_expression) or SubstrExpr(column, value_expression,value_expression)
	// also supported syntax SubstrExpr(column from value_expression for value_expression).
	// Additionally to column names, SubstrExpr is also supported for string values, e.g.:
	// SubstrExpr('static string value', value_expression, value_expression)
	// In this case StrVal will be set instead of Name.
	SubstrExpr struct {
		Name   *ColName
		StrVal *Literal
		From   Expr
		To     Expr
	}

	// ConvertExpr represents a call to CONVERT(expr, type)
	// or it's equivalent CAST(expr AS type). Both are rewritten to the former.
	ConvertExpr struct {
		Expr Expr
		Type *ConvertType
	}

	// ConvertUsingExpr represents a call to CONVERT(expr USING charset).
	ConvertUsingExpr struct {
		Expr Expr
		Type string
	}

	// MatchExpr represents a call to the MATCH function
	MatchExpr struct {
		Columns SelectExprs
		Expr    Expr
		Option  MatchExprOption
	}

	// MatchExprOption is an enum for MatchExpr.Option
	MatchExprOption int8

	// CaseExpr represents a CASE expression.
	CaseExpr struct {
		Expr  Expr
		Whens []*When
		Else  Expr
	}

	// Default represents a DEFAULT expression.
	Default struct {
		ColName string
	}

	// When represents a WHEN sub-expression.
	When struct {
		Cond Expr
		Val  Expr
	}

	// CurTimeFuncExpr represents the function and arguments for CURRENT DATE/TIME functions
	// supported functions are documented in the grammar
	CurTimeFuncExpr struct {
		Name ColIdent
		Fsp  Expr // fractional seconds precision, integer from 0 to 6
	}
)

// iExpr ensures that only expressions nodes can be assigned to a Expr
func (*AndExpr) iExpr()           {}
func (*OrExpr) iExpr()            {}
func (*XorExpr) iExpr()           {}
func (*NotExpr) iExpr()           {}
func (*ComparisonExpr) iExpr()    {}
func (*RangeCond) iExpr()         {}
func (*IsExpr) iExpr()            {}
func (*ExistsExpr) iExpr()        {}
func (*Literal) iExpr()           {}
func (Argument) iExpr()           {}
func (*NullVal) iExpr()           {}
func (BoolVal) iExpr()            {}
func (*ColName) iExpr()           {}
func (ValTuple) iExpr()           {}
func (*Subquery) iExpr()          {}
func (ListArg) iExpr()            {}
func (*BinaryExpr) iExpr()        {}
func (*UnaryExpr) iExpr()         {}
func (*IntervalExpr) iExpr()      {}
func (*CollateExpr) iExpr()       {}
func (*FuncExpr) iExpr()          {}
func (*TimestampFuncExpr) iExpr() {}
func (*CurTimeFuncExpr) iExpr()   {}
func (*CaseExpr) iExpr()          {}
func (*ValuesFuncExpr) iExpr()    {}
func (*ConvertExpr) iExpr()       {}
func (*SubstrExpr) iExpr()        {}
func (*ConvertUsingExpr) iExpr()  {}
func (*MatchExpr) iExpr()         {}
func (*GroupConcatExpr) iExpr()   {}
func (*Default) iExpr()           {}

// Exprs represents a list of value expressions.
// It's not a valid expression because it's not parenthesized.
type Exprs []Expr

func (ValTuple) iColTuple()  {}
func (*Subquery) iColTuple() {}
func (ListArg) iColTuple()   {}

// ConvertType represents the type in call to CONVERT(expr, type)
type ConvertType struct {
	Type     string
	Length   *Literal
	Scale    *Literal
	Operator ConvertTypeOperator
	Charset  string
}

// ConvertTypeOperator is an enum for ConvertType.Operator
type ConvertTypeOperator int8

// GroupBy represents a GROUP BY clause.
type GroupBy []Expr

// OrderBy represents an ORDER By clause.
type OrderBy []*Order

// Order represents an ordering expression.
type Order struct {
	Expr      Expr
	Direction OrderDirection
}

// OrderDirection is an enum for Order.Direction
type OrderDirection int8

// Limit represents a LIMIT clause.
type Limit struct {
	Offset, Rowcount Expr
}

// Values represents a VALUES clause.
type Values []ValTuple

// UpdateExprs represents a list of update expressions.
type UpdateExprs []*UpdateExpr

// UpdateExpr represents an update expression.
type UpdateExpr struct {
	Name *ColName
	Expr Expr
}

// SetExprs represents a list of set expressions.
type SetExprs []*SetExpr

// SetExpr represents a set expression.
type SetExpr struct {
	Scope Scope
	Name  ColIdent
	Expr  Expr
}

// OnDup represents an ON DUPLICATE KEY clause.
type OnDup UpdateExprs

// ColIdent is a case insensitive SQL identifier. It will be escaped with
// backquotes if necessary.
type ColIdent struct {
	// This artifact prevents this struct from being compared
	// with itself. It consumes no space as long as it's not the
	// last field in the struct.
	_            [0]struct{ _ []byte }
	val, lowered string
	at           AtCount
}

// TableIdent is a case sensitive SQL identifier. It will be escaped with
// backquotes if necessary.
type TableIdent struct {
	v string
}

// Here follow all the Format implementations for AST nodes

// Format formats the node.
func (node *Select) Format(buf *TrackedBuffer) {
	var options string
	addIf := func(b bool, s string) {
		if b {
			options += s
		}
	}
	addIf(node.Distinct, DistinctStr)
	if node.Cache != nil {
		if *node.Cache {
			options += SQLCacheStr
		} else {
			options += SQLNoCacheStr
		}
	}
	addIf(node.StraightJoinHint, StraightJoinHint)
	addIf(node.SQLCalcFoundRows, SQLCalcFoundRowsStr)

	buf.astPrintf(node, "select %v%s%v from %v%v%v%v%v%v%s",
		node.Comments, options, node.SelectExprs,
		node.From, node.Where,
		node.GroupBy, node.Having, node.OrderBy,
		node.Limit, node.Lock.ToString())
	if node.IntoOutfileS3 != "" {
		buf.astPrintf(node, " into outfile s3 '%s'", node.IntoOutfileS3)
	}
}

// Format formats the node.
func (node *ParenSelect) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "(%v)", node.Select)
}

// Format formats the node.
func (node *Union) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%v", node.FirstStatement)
	for _, us := range node.UnionSelects {
		buf.astPrintf(node, "%v", us)
	}
	buf.astPrintf(node, "%v%v%s", node.OrderBy, node.Limit, node.Lock.ToString())
}

// Format formats the node.
func (node *UnionSelect) Format(buf *TrackedBuffer) {
	switch node.Type {
	case UnionBasic:
		buf.astPrintf(node, " %s %v", UnionStr, node.Statement)
	case UnionAll:
		buf.astPrintf(node, " %s %v", UnionAllStr, node.Statement)
	case UnionDistinct:
		buf.astPrintf(node, " %s %v", UnionDistinctStr, node.Statement)
	default:
		buf.astPrintf(node, " %s %v", "Unknown Union Type", node.Statement)
	}
}

// Format formats the node.
func (node *VStream) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "vstream %v%v from %v",
		node.Comments, node.SelectExpr, node.Table)
}

// Format formats the node.
func (node *Stream) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "stream %v%v from %v",
		node.Comments, node.SelectExpr, node.Table)
}

// Format formats the node.
func (node *Insert) Format(buf *TrackedBuffer) {
	switch node.Action {
	case InsertAct:
		buf.astPrintf(node, "%s %v%sinto %v%v%v %v%v",
			InsertStr,
			node.Comments, node.Ignore.ToString(),
			node.Table, node.Partitions, node.Columns, node.Rows, node.OnDup)
	case ReplaceAct:
		buf.astPrintf(node, "%s %v%sinto %v%v%v %v%v",
			ReplaceStr,
			node.Comments, node.Ignore.ToString(),
			node.Table, node.Partitions, node.Columns, node.Rows, node.OnDup)
	default:
		buf.astPrintf(node, "%s %v%sinto %v%v%v %v%v",
			"Unkown Insert Action",
			node.Comments, node.Ignore.ToString(),
			node.Table, node.Partitions, node.Columns, node.Rows, node.OnDup)
	}

}

// Format formats the node.
func (node *Update) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "update %v%s%v set %v%v%v%v",
		node.Comments, node.Ignore.ToString(), node.TableExprs,
		node.Exprs, node.Where, node.OrderBy, node.Limit)
}

// Format formats the node.
func (node *Delete) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "delete %v", node.Comments)
	if node.Ignore {
		buf.WriteString("ignore ")
	}
	if node.Targets != nil {
		buf.astPrintf(node, "%v ", node.Targets)
	}
	buf.astPrintf(node, "from %v%v%v%v%v", node.TableExprs, node.Partitions, node.Where, node.OrderBy, node.Limit)
}

// Format formats the node.
func (node *Set) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "set %v%v", node.Comments, node.Exprs)
}

// Format formats the node.
func (node *SetTransaction) Format(buf *TrackedBuffer) {
	if node.Scope == ImplicitScope {
		buf.astPrintf(node, "set %vtransaction ", node.Comments)
	} else {
		buf.astPrintf(node, "set %v%s transaction ", node.Comments, node.Scope.ToString())
	}

	for i, char := range node.Characteristics {
		if i > 0 {
			buf.WriteString(", ")
		}
		buf.astPrintf(node, "%v", char)
	}
}

// Format formats the node.
func (node *DBDDL) Format(buf *TrackedBuffer) {
	switch node.Action {
	case CreateDBDDLAction:
		notExists := ""
		if node.IfNotExists {
			notExists = " if not exists"
		}
		buf.WriteString(fmt.Sprintf("%s database%s %v", CreateStr, notExists, node.DBName))
	case AlterDBDDLAction:
		buf.WriteString(fmt.Sprintf("%s database %s", AlterStr, node.DBName))
	case DropDBDDLAction:
		exists := ""
		if node.IfExists {
			exists = " if exists"
		}
		buf.WriteString(fmt.Sprintf("%s database%s %v", DropStr, exists, node.DBName))
	}
}

// Format formats the node.
func (node *DDL) Format(buf *TrackedBuffer) {
	switch node.Action {
	case CreateDDLAction:
		if node.OptLike != nil {
			buf.astPrintf(node, "%s table %v %v", CreateStr, node.Table, node.OptLike)
		} else if node.TableSpec != nil {
			buf.astPrintf(node, "%s table %v %v", CreateStr, node.Table, node.TableSpec)
		} else {
			buf.astPrintf(node, "%s table %v", CreateStr, node.Table)
		}
	case DropDDLAction:
		exists := ""
		if node.IfExists {
			exists = " if exists"
		}
		buf.astPrintf(node, "%s table%s %v", DropStr, exists, node.FromTables)
	case RenameDDLAction:
		buf.astPrintf(node, "%s table %v to %v", RenameStr, node.FromTables[0], node.ToTables[0])
		for i := 1; i < len(node.FromTables); i++ {
			buf.astPrintf(node, ", %v to %v", node.FromTables[i], node.ToTables[i])
		}
	case AlterDDLAction:
		if node.PartitionSpec != nil {
			buf.astPrintf(node, "%s table %v %v", AlterStr, node.Table, node.PartitionSpec)
		} else {
			buf.astPrintf(node, "%s table %v", AlterStr, node.Table)
		}
	case FlushDDLAction:
		buf.astPrintf(node, "%s", FlushStr)
	case CreateVindexDDLAction:
		buf.astPrintf(node, "alter vschema create vindex %v %v", node.Table, node.VindexSpec)
	case DropVindexDDLAction:
		buf.astPrintf(node, "alter vschema drop vindex %v", node.Table)
	case AddVschemaTableDDLAction:
		buf.astPrintf(node, "alter vschema add table %v", node.Table)
	case DropVschemaTableDDLAction:
		buf.astPrintf(node, "alter vschema drop table %v", node.Table)
	case AddColVindexDDLAction:
		buf.astPrintf(node, "alter vschema on %v add vindex %v (", node.Table, node.VindexSpec.Name)
		for i, col := range node.VindexCols {
			if i != 0 {
				buf.astPrintf(node, ", %v", col)
			} else {
				buf.astPrintf(node, "%v", col)
			}
		}
		buf.astPrintf(node, ")")
		if node.VindexSpec.Type.String() != "" {
			buf.astPrintf(node, " %v", node.VindexSpec)
		}
	case DropColVindexDDLAction:
		buf.astPrintf(node, "alter vschema on %v drop vindex %v", node.Table, node.VindexSpec.Name)
	case AddSequenceDDLAction:
		buf.astPrintf(node, "alter vschema add sequence %v", node.Table)
	case AddAutoIncDDLAction:
		buf.astPrintf(node, "alter vschema on %v add auto_increment %v", node.Table, node.AutoIncSpec)
	default:
		buf.astPrintf(node, "%s table %v", node.Action.ToString(), node.Table)
	}
}

// Format formats the node.
func (node *OptLike) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "like %v", node.LikeTable)
}

// Format formats the node.
func (node *PartitionSpec) Format(buf *TrackedBuffer) {
	switch node.Action {
	case ReorganizeAction:
		buf.astPrintf(node, "%s %v into (", ReorganizeStr, node.Name)
		var prefix string
		for _, pd := range node.Definitions {
			buf.astPrintf(node, "%s%v", prefix, pd)
			prefix = ", "
		}
		buf.astPrintf(node, ")")
	default:
		panic("unimplemented")
	}
}

// Format formats the node
func (node *PartitionDefinition) Format(buf *TrackedBuffer) {
	if !node.Maxvalue {
		buf.astPrintf(node, "partition %v values less than (%v)", node.Name, node.Limit)
	} else {
		buf.astPrintf(node, "partition %v values less than (maxvalue)", node.Name)
	}
}

// Format formats the node.
func (ts *TableSpec) Format(buf *TrackedBuffer) {
	buf.astPrintf(ts, "(\n")
	for i, col := range ts.Columns {
		if i == 0 {
			buf.astPrintf(ts, "\t%v", col)
		} else {
			buf.astPrintf(ts, ",\n\t%v", col)
		}
	}
	for _, idx := range ts.Indexes {
		buf.astPrintf(ts, ",\n\t%v", idx)
	}
	for _, c := range ts.Constraints {
		buf.astPrintf(ts, ",\n\t%v", c)
	}

	buf.astPrintf(ts, "\n)%s", strings.Replace(ts.Options, ", ", ",\n  ", -1))
}

// Format formats the node.
func (col *ColumnDefinition) Format(buf *TrackedBuffer) {
	buf.astPrintf(col, "%v %v", col.Name, &col.Type)
}

// Format returns a canonical string representation of the type and all relevant options
func (ct *ColumnType) Format(buf *TrackedBuffer) {
	buf.astPrintf(ct, "%s", ct.Type)

	if ct.Length != nil && ct.Scale != nil {
		buf.astPrintf(ct, "(%v,%v)", ct.Length, ct.Scale)

	} else if ct.Length != nil {
		buf.astPrintf(ct, "(%v)", ct.Length)
	}

	if ct.EnumValues != nil {
		buf.astPrintf(ct, "(%s)", strings.Join(ct.EnumValues, ", "))
	}

	opts := make([]string, 0, 16)
	if ct.Unsigned {
		opts = append(opts, keywordStrings[UNSIGNED])
	}
	if ct.Zerofill {
		opts = append(opts, keywordStrings[ZEROFILL])
	}
	if ct.Charset != "" {
		opts = append(opts, keywordStrings[CHARACTER], keywordStrings[SET], ct.Charset)
	}
	if ct.Collate != "" {
		opts = append(opts, keywordStrings[COLLATE], ct.Collate)
	}
	if ct.NotNull {
		opts = append(opts, keywordStrings[NOT], keywordStrings[NULL])
	}
	if ct.Default != nil {
		opts = append(opts, keywordStrings[DEFAULT], String(ct.Default))
	}
	if ct.OnUpdate != nil {
		opts = append(opts, keywordStrings[ON], keywordStrings[UPDATE], String(ct.OnUpdate))
	}
	if ct.Autoincrement {
		opts = append(opts, keywordStrings[AUTO_INCREMENT])
	}
	if ct.Comment != nil {
		opts = append(opts, keywordStrings[COMMENT_KEYWORD], String(ct.Comment))
	}
	if ct.KeyOpt == colKeyPrimary {
		opts = append(opts, keywordStrings[PRIMARY], keywordStrings[KEY])
	}
	if ct.KeyOpt == colKeyUnique {
		opts = append(opts, keywordStrings[UNIQUE])
	}
	if ct.KeyOpt == colKeyUniqueKey {
		opts = append(opts, keywordStrings[UNIQUE], keywordStrings[KEY])
	}
	if ct.KeyOpt == colKeySpatialKey {
		opts = append(opts, keywordStrings[SPATIAL], keywordStrings[KEY])
	}
	if ct.KeyOpt == colKey {
		opts = append(opts, keywordStrings[KEY])
	}

	if len(opts) != 0 {
		buf.astPrintf(ct, " %s", strings.Join(opts, " "))
	}
}

// Format formats the node.
func (idx *IndexDefinition) Format(buf *TrackedBuffer) {
	buf.astPrintf(idx, "%v (", idx.Info)
	for i, col := range idx.Columns {
		if i != 0 {
			buf.astPrintf(idx, ", %v", col.Column)
		} else {
			buf.astPrintf(idx, "%v", col.Column)
		}
		if col.Length != nil {
			buf.astPrintf(idx, "(%v)", col.Length)
		}
	}
	buf.astPrintf(idx, ")")

	for _, opt := range idx.Options {
		buf.astPrintf(idx, " %s", opt.Name)
		if opt.Using != "" {
			buf.astPrintf(idx, " %s", opt.Using)
		} else {
			buf.astPrintf(idx, " %v", opt.Value)
		}
	}
}

// Format formats the node.
func (ii *IndexInfo) Format(buf *TrackedBuffer) {
	if ii.Primary {
		buf.astPrintf(ii, "%s", ii.Type)
	} else {
		buf.astPrintf(ii, "%s", ii.Type)
		if !ii.Name.IsEmpty() {
			buf.astPrintf(ii, " %v", ii.Name)
		}
	}
}

// Format formats the node.
func (node *AutoIncSpec) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%v ", node.Column)
	buf.astPrintf(node, "using %v", node.Sequence)
}

// Format formats the node. The "CREATE VINDEX" preamble was formatted in
// the containing DDL node Format, so this just prints the type, any
// parameters, and optionally the owner
func (node *VindexSpec) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "using %v", node.Type)

	numParams := len(node.Params)
	if numParams != 0 {
		buf.astPrintf(node, " with ")
		for i, p := range node.Params {
			if i != 0 {
				buf.astPrintf(node, ", ")
			}
			buf.astPrintf(node, "%v", p)
		}
	}
}

// Format formats the node.
func (node VindexParam) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%s=%s", node.Key.String(), node.Val)
}

// Format formats the node.
func (c *ConstraintDefinition) Format(buf *TrackedBuffer) {
	if c.Name != "" {
		buf.astPrintf(c, "constraint %s ", c.Name)
	}
	c.Details.Format(buf)
}

// Format formats the node.
func (a ReferenceAction) Format(buf *TrackedBuffer) {
	switch a {
	case Restrict:
		buf.WriteString("restrict")
	case Cascade:
		buf.WriteString("cascade")
	case NoAction:
		buf.WriteString("no action")
	case SetNull:
		buf.WriteString("set null")
	case SetDefault:
		buf.WriteString("set default")
	}
}

// Format formats the node.
func (f *ForeignKeyDefinition) Format(buf *TrackedBuffer) {
	buf.astPrintf(f, "foreign key %v references %v %v", f.Source, f.ReferencedTable, f.ReferencedColumns)
	if f.OnDelete != DefaultAction {
		buf.astPrintf(f, " on delete %v", f.OnDelete)
	}
	if f.OnUpdate != DefaultAction {
		buf.astPrintf(f, " on update %v", f.OnUpdate)
	}
}

// Format formats the node.
func (node *Show) Format(buf *TrackedBuffer) {
	nodeType := strings.ToLower(node.Type)
	if (nodeType == "tables" || nodeType == "columns" || nodeType == "fields" || nodeType == "index" || nodeType == "keys" || nodeType == "indexes" ||
		nodeType == "databases" || nodeType == "keyspaces" || nodeType == "vitess_keyspaces" || nodeType == "vitess_shards" || nodeType == "vitess_tablets") && node.ShowTablesOpt != nil {
		opt := node.ShowTablesOpt
		if node.Extended != "" {
			buf.astPrintf(node, "show %s%s", node.Extended, nodeType)
		} else {
			buf.astPrintf(node, "show %s%s", opt.Full, nodeType)
		}
		if (nodeType == "columns" || nodeType == "fields") && node.HasOnTable() {
			buf.astPrintf(node, " from %v", node.OnTable)
		}
		if (nodeType == "index" || nodeType == "keys" || nodeType == "indexes") && node.HasOnTable() {
			buf.astPrintf(node, " from %v", node.OnTable)
		}
		if opt.DbName != "" {
			buf.astPrintf(node, " from %s", opt.DbName)
		}
		buf.astPrintf(node, "%v", opt.Filter)
		return
	}
	if node.Scope == ImplicitScope {
		buf.astPrintf(node, "show %s", nodeType)
	} else {
		buf.astPrintf(node, "show %s %s", node.Scope.ToString(), nodeType)
	}
	if node.HasOnTable() {
		buf.astPrintf(node, " on %v", node.OnTable)
	}
	if nodeType == "collation" && node.ShowCollationFilterOpt != nil {
		buf.astPrintf(node, " where %v", node.ShowCollationFilterOpt)
	}
	if nodeType == "charset" && node.ShowTablesOpt != nil {
		buf.astPrintf(node, "%v", node.ShowTablesOpt.Filter)
	}
	if node.HasTable() {
		buf.astPrintf(node, " %v", node.Table)
	}
}

// Format formats the node.
func (node *ShowFilter) Format(buf *TrackedBuffer) {
	if node == nil {
		return
	}
	if node.Like != "" {
		buf.astPrintf(node, " like '%s'", node.Like)
	} else {
		buf.astPrintf(node, " where %v", node.Filter)
	}
}

// Format formats the node.
func (node *Use) Format(buf *TrackedBuffer) {
	if node.DBName.v != "" {
		buf.astPrintf(node, "use %v", node.DBName)
	} else {
		buf.astPrintf(node, "use")
	}
}

// Format formats the node.
func (node *Commit) Format(buf *TrackedBuffer) {
	buf.WriteString("commit")
}

// Format formats the node.
func (node *Begin) Format(buf *TrackedBuffer) {
	buf.WriteString("begin")
}

// Format formats the node.
func (node *Rollback) Format(buf *TrackedBuffer) {
	buf.WriteString("rollback")
}

// Format formats the node.
func (node *SRollback) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "rollback to %v", node.Name)
}

// Format formats the node.
func (node *Savepoint) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "savepoint %v", node.Name)
}

// Format formats the node.
func (node *Release) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "release savepoint %v", node.Name)
}

// Format formats the node.
func (node *Explain) Format(buf *TrackedBuffer) {
	format := ""
	switch node.Type {
	case EmptyType: // do nothing
	case AnalyzeType:
		format = AnalyzeStr + " "
	default:
		format = "format = " + node.Type.ToString() + " "
	}
	buf.astPrintf(node, "explain %s%v", format, node.Statement)
}

// Format formats the node.
func (node *OtherRead) Format(buf *TrackedBuffer) {
	buf.WriteString("otherread")
}

// Format formats the node.
func (node *OtherAdmin) Format(buf *TrackedBuffer) {
	buf.WriteString("otheradmin")
}

// Format formats the node.
func (node Comments) Format(buf *TrackedBuffer) {
	for _, c := range node {
		buf.astPrintf(node, "%s ", c)
	}
}

// Format formats the node.
func (node SelectExprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *StarExpr) Format(buf *TrackedBuffer) {
	if !node.TableName.IsEmpty() {
		buf.astPrintf(node, "%v.", node.TableName)
	}
	buf.astPrintf(node, "*")
}

// Format formats the node.
func (node *AliasedExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%v", node.Expr)
	if !node.As.IsEmpty() {
		buf.astPrintf(node, " as %v", node.As)
	}
}

// Format formats the node.
func (node Nextval) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "next %v values", node.Expr)
}

// Format formats the node.
func (node Columns) Format(buf *TrackedBuffer) {
	if node == nil {
		return
	}
	prefix := "("
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
	buf.WriteString(")")
}

// Format formats the node
func (node Partitions) Format(buf *TrackedBuffer) {
	if node == nil {
		return
	}
	prefix := " partition ("
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
	buf.WriteString(")")
}

// Format formats the node.
func (node TableExprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *AliasedTableExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%v%v", node.Expr, node.Partitions)
	if !node.As.IsEmpty() {
		buf.astPrintf(node, " as %v", node.As)
	}
	if node.Hints != nil {
		// Hint node provides the space padding.
		buf.astPrintf(node, "%v", node.Hints)
	}
}

// Format formats the node.
func (node TableNames) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node TableName) Format(buf *TrackedBuffer) {
	if node.IsEmpty() {
		return
	}
	if !node.Qualifier.IsEmpty() {
		buf.astPrintf(node, "%v.", node.Qualifier)
	}
	buf.astPrintf(node, "%v", node.Name)
}

// Format formats the node.
func (node *ParenTableExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "(%v)", node.Exprs)
}

// Format formats the node.
func (node JoinCondition) Format(buf *TrackedBuffer) {
	if node.On != nil {
		buf.astPrintf(node, " on %v", node.On)
	}
	if node.Using != nil {
		buf.astPrintf(node, " using %v", node.Using)
	}
}

// Format formats the node.
func (node *JoinTableExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%v %s %v%v", node.LeftExpr, node.Join.ToString(), node.RightExpr, node.Condition)
}

// Format formats the node.
func (node *IndexHints) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, " %sindex ", node.Type.ToString())
	if len(node.Indexes) == 0 {
		buf.astPrintf(node, "()")
	} else {
		prefix := "("
		for _, n := range node.Indexes {
			buf.astPrintf(node, "%s%v", prefix, n)
			prefix = ", "
		}
		buf.astPrintf(node, ")")
	}
}

// Format formats the node.
func (node *Where) Format(buf *TrackedBuffer) {
	if node == nil || node.Expr == nil {
		return
	}
	buf.astPrintf(node, " %s %v", node.Type.ToString(), node.Expr)
}

// Format formats the node.
func (node Exprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *AndExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%l and %r", node.Left, node.Right)
}

// Format formats the node.
func (node *OrExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%l or %r", node.Left, node.Right)
}

// Format formats the node.
func (node *XorExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%l xor %r", node.Left, node.Right)
}

// Format formats the node.
func (node *NotExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "not %v", node.Expr)
}

// Format formats the node.
func (node *ComparisonExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%l %s %r", node.Left, node.Operator.ToString(), node.Right)
	if node.Escape != nil {
		buf.astPrintf(node, " escape %v", node.Escape)
	}
}

// Format formats the node.
func (node *RangeCond) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%v %s %l and %r", node.Left, node.Operator.ToString(), node.From, node.To)
}

// Format formats the node.
func (node *IsExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%v %s", node.Expr, node.Operator.ToString())
}

// Format formats the node.
func (node *ExistsExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "exists %v", node.Subquery)
}

// Format formats the node.
func (node *Literal) Format(buf *TrackedBuffer) {
	switch node.Type {
	case StrVal:
		sqltypes.MakeTrusted(sqltypes.VarBinary, node.Val).EncodeSQL(buf)
	case IntVal, FloatVal, HexNum:
		buf.astPrintf(node, "%s", node.Val)
	case HexVal:
		buf.astPrintf(node, "X'%s'", node.Val)
	case BitVal:
		buf.astPrintf(node, "B'%s'", node.Val)
	default:
		panic("unexpected")
	}
}

// Format formats the node.
func (node Argument) Format(buf *TrackedBuffer) {
	buf.WriteArg(string(node))
}

// Format formats the node.
func (node *NullVal) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "null")
}

// Format formats the node.
func (node BoolVal) Format(buf *TrackedBuffer) {
	if node {
		buf.astPrintf(node, "true")
	} else {
		buf.astPrintf(node, "false")
	}
}

// Format formats the node.
func (node *ColName) Format(buf *TrackedBuffer) {
	if !node.Qualifier.IsEmpty() {
		buf.astPrintf(node, "%v.", node.Qualifier)
	}
	buf.astPrintf(node, "%v", node.Name)
}

// Format formats the node.
func (node ValTuple) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "(%v)", Exprs(node))
}

// Format formats the node.
func (node *Subquery) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "(%v)", node.Select)
}

// Format formats the node.
func (node ListArg) Format(buf *TrackedBuffer) {
	buf.WriteArg(string(node))
}

// Format formats the node.
func (node *BinaryExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%l %s %r", node.Left, node.Operator.ToString(), node.Right)
}

// Format formats the node.
func (node *UnaryExpr) Format(buf *TrackedBuffer) {
	if _, unary := node.Expr.(*UnaryExpr); unary {
		// They have same precedence so parenthesis is not required.
		buf.astPrintf(node, "%s %v", node.Operator.ToString(), node.Expr)
		return
	}
	buf.astPrintf(node, "%s%v", node.Operator.ToString(), node.Expr)
}

// Format formats the node.
func (node *IntervalExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "interval %v %s", node.Expr, node.Unit)
}

// Format formats the node.
func (node *TimestampFuncExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%s(%s, %v, %v)", node.Name, node.Unit, node.Expr1, node.Expr2)
}

// Format formats the node.
func (node *CurTimeFuncExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%s(%v)", node.Name.String(), node.Fsp)
}

// Format formats the node.
func (node *CollateExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%v collate %s", node.Expr, node.Charset)
}

// Format formats the node.
func (node *FuncExpr) Format(buf *TrackedBuffer) {
	var distinct string
	if node.Distinct {
		distinct = "distinct "
	}
	if !node.Qualifier.IsEmpty() {
		buf.astPrintf(node, "%v.", node.Qualifier)
	}
	// Function names should not be back-quoted even
	// if they match a reserved word, only if they contain illegal characters
	funcName := node.Name.String()

	if containEscapableChars(funcName, NoAt) {
		writeEscapedString(buf, funcName)
	} else {
		buf.WriteString(funcName)
	}
	buf.astPrintf(node, "(%s%v)", distinct, node.Exprs)
}

// Format formats the node
func (node *GroupConcatExpr) Format(buf *TrackedBuffer) {
	if node.Distinct {
		buf.astPrintf(node, "group_concat(%s%v%v%s%v)", DistinctStr, node.Exprs, node.OrderBy, node.Separator, node.Limit)
	} else {
		buf.astPrintf(node, "group_concat(%v%v%s%v)", node.Exprs, node.OrderBy, node.Separator, node.Limit)
	}
}

// Format formats the node.
func (node *ValuesFuncExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "values(%v)", node.Name)
}

// Format formats the node.
func (node *SubstrExpr) Format(buf *TrackedBuffer) {
	var val interface{}
	if node.Name != nil {
		val = node.Name
	} else {
		val = node.StrVal
	}

	if node.To == nil {
		buf.astPrintf(node, "substr(%v, %v)", val, node.From)
	} else {
		buf.astPrintf(node, "substr(%v, %v, %v)", val, node.From, node.To)
	}
}

// Format formats the node.
func (node *ConvertExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "convert(%v, %v)", node.Expr, node.Type)
}

// Format formats the node.
func (node *ConvertUsingExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "convert(%v using %s)", node.Expr, node.Type)
}

// Format formats the node.
func (node *ConvertType) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%s", node.Type)
	if node.Length != nil {
		buf.astPrintf(node, "(%v", node.Length)
		if node.Scale != nil {
			buf.astPrintf(node, ", %v", node.Scale)
		}
		buf.astPrintf(node, ")")
	}
	if node.Charset != "" {
		buf.astPrintf(node, "%s %s", node.Operator.ToString(), node.Charset)
	}
}

// Format formats the node
func (node *MatchExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "match(%v) against (%v%s)", node.Columns, node.Expr, node.Option.ToString())
}

// Format formats the node.
func (node *CaseExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "case ")
	if node.Expr != nil {
		buf.astPrintf(node, "%v ", node.Expr)
	}
	for _, when := range node.Whens {
		buf.astPrintf(node, "%v ", when)
	}
	if node.Else != nil {
		buf.astPrintf(node, "else %v ", node.Else)
	}
	buf.astPrintf(node, "end")
}

// Format formats the node.
func (node *Default) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "default")
	if node.ColName != "" {
		buf.WriteString("(")
		formatID(buf, node.ColName, strings.ToLower(node.ColName), NoAt)
		buf.WriteString(")")
	}
}

// Format formats the node.
func (node *When) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "when %v then %v", node.Cond, node.Val)
}

// Format formats the node.
func (node GroupBy) Format(buf *TrackedBuffer) {
	prefix := " group by "
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node OrderBy) Format(buf *TrackedBuffer) {
	prefix := " order by "
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *Order) Format(buf *TrackedBuffer) {
	if node, ok := node.Expr.(*NullVal); ok {
		buf.astPrintf(node, "%v", node)
		return
	}
	if node, ok := node.Expr.(*FuncExpr); ok {
		if node.Name.Lowered() == "rand" {
			buf.astPrintf(node, "%v", node)
			return
		}
	}

	buf.astPrintf(node, "%v %s", node.Expr, node.Direction.ToString())
}

// Format formats the node.
func (node *Limit) Format(buf *TrackedBuffer) {
	if node == nil {
		return
	}
	buf.astPrintf(node, " limit ")
	if node.Offset != nil {
		buf.astPrintf(node, "%v, ", node.Offset)
	}
	buf.astPrintf(node, "%v", node.Rowcount)
}

// Format formats the node.
func (node Values) Format(buf *TrackedBuffer) {
	prefix := "values "
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node UpdateExprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *UpdateExpr) Format(buf *TrackedBuffer) {
	buf.astPrintf(node, "%v = %v", node.Name, node.Expr)
}

// Format formats the node.
func (node SetExprs) Format(buf *TrackedBuffer) {
	var prefix string
	for _, n := range node {
		buf.astPrintf(node, "%s%v", prefix, n)
		prefix = ", "
	}
}

// Format formats the node.
func (node *SetExpr) Format(buf *TrackedBuffer) {
	if node.Scope != ImplicitScope {
		buf.WriteString(node.Scope.ToString())
		buf.WriteString(" ")
	}
	// We don't have to backtick set variable names.
	switch {
	case node.Name.EqualString("charset") || node.Name.EqualString("names"):
		buf.astPrintf(node, "%s %v", node.Name.String(), node.Expr)
	case node.Name.EqualString(TransactionStr):
		literal := node.Expr.(*Literal)
		buf.astPrintf(node, "%s %s", node.Name.String(), strings.ToLower(string(literal.Val)))
	default:
		buf.astPrintf(node, "%v = %v", node.Name, node.Expr)
	}
}

// Format formats the node.
func (node OnDup) Format(buf *TrackedBuffer) {
	if node == nil {
		return
	}
	buf.astPrintf(node, " on duplicate key update %v", UpdateExprs(node))
}

// Format formats the node.
func (node ColIdent) Format(buf *TrackedBuffer) {
	for i := NoAt; i < node.at; i++ {
		buf.WriteByte('@')
	}
	formatID(buf, node.val, node.Lowered(), node.at)
}

// Format formats the node.
func (node TableIdent) Format(buf *TrackedBuffer) {
	formatID(buf, node.v, strings.ToLower(node.v), NoAt)
}

// AtCount return the '@' count present in ColIdent Name
func (node ColIdent) AtCount() AtCount {
	return node.at
}

func (IsolationLevel) iChar() {}
func (AccessMode) iChar()     {}

// Format formats the node.
func (node IsolationLevel) Format(buf *TrackedBuffer) {
	buf.WriteString("isolation level ")
	switch node {
	case ReadUncommitted:
		buf.WriteString(ReadUncommittedStr)
	case ReadCommitted:
		buf.WriteString(ReadCommittedStr)
	case RepeatableRead:
		buf.WriteString(RepeatableReadStr)
	case Serializable:
		buf.WriteString(SerializableStr)
	default:
		buf.WriteString("Unknown Isolation level value")
	}
}

// Format formats the node.
func (node AccessMode) Format(buf *TrackedBuffer) {
	if node == ReadOnly {
		buf.WriteString(TxReadOnly)
	} else {
		buf.WriteString(TxReadWrite)
	}
}

// Format formats the node.
func (node *ShowTableStatus) Format(buf *TrackedBuffer) {
	buf.WriteString("show table status")
	if node.DatabaseName != "" {
		buf.WriteString(" from ")
		buf.WriteString(node.DatabaseName)
	}
	buf.astPrintf(node, "%v", node.Filter)
}
